function [prm]=bciP3Control(varargin)
% [prm]=bciP3Control(bci,action,prm)
% controls the stimulus and its parameters
% bci.paradigm.id - 12 = P3 object selection
%                   14 = P3 object selection + grasping

if length(varargin)==3,
    switch varargin{1}.paradigm.id,
        case 12,
            %             prm=bciParadigmID11(bci,action,prm);
            prm=bciParadigmID12(varargin{1},varargin{2},varargin{3});
        case 14,
            prm=bciParadigmID14(varargin{:});
    end
else
    bciprm = bciGetParam;
    if (nargin==1),         
        objAlignment = varargin{1};
        if length(objAlignment)>1 && strcmp(objAlignment(2),'g'),
            mode=3;
            bciprm.paradigm.objectAlignment = objAlignment(1);
        else
            mode=0;
        end
    else
        objAlignment=bciprm.paradigm.objectAlignment;
        mode = 0;
    end
    if strcmp(objAlignment(1),'-')||strcmpi(objAlignment(1),'o'),
        prm = bciParadigmID14(bciprm,'init',struct('mode',mode)); %mode 3 precalculates grasps
%         prm = bciParadigmID14(struct('paradigm',struct('objectAlignment',objAlignment,'nTargetFlashes',5),...
%             'numTrials',0,'movDur',5.5,'eventsToClassify',1:6),'init',struct('mode',mode)); %mode 3 precalculates grasps
    else
        prm = bciParadigmID12(bciprm,'init',struct('mode',0));
%         prm = bciParadigmID12(struct('paradigm',struct('objectAlignment',objAlignment),...
%             'numTrials',0,'movDur',5.5),'init',struct('mode',0));
    end
end


% paradigm dependent functions
% P300 selection task
function prm=bciParadigmID12(bci,action,prm)
if strcmp(action,'flash'),
    % determine reference object to ensure synchrony of flashes
    refObj = find(prm.flashSequenceMat(prm.flashCount,:,prm.trialCount),1);
    for objCount=1:prm.nObjects,
        if prm.flashSequenceMat(prm.flashCount,objCount,prm.trialCount) && objCount~=refObj,
            % couple commonly flashing objects
            setRESI('coupleObjectFrequency','idx',prm.objID(objCount),'otheridx',prm.objID(refObj));
        elseif prm.lastFlashSequence(objCount),
            % decouple previous couplings
            setRESI('coupleObjectFrequency','idx',prm.objID(objCount),'otheridx',-1);
        end
    end
    prm.lastFlashSequence = prm.flashSequenceMat(prm.flashCount,:,prm.trialCount);
    prm.flashCount = prm.flashCount+1;
    if ~isempty(refObj),
        setRESI('flashObject','idx',prm.objID(refObj),'minVisibleFrames',prm.maxVisibleFrames);
    end
    %     prm.nextFlashTime = prm.nextFlashTime+prm.flashDelay;
elseif strcmp(action,'select'),
    % stop flash sequence
    if prm.curEvent ~= 8,
        prm.hitTarget=false;
        prm.mostPred = 0;
        return
    end
    if prm.mode>0 && ~prm.trialClassified && ~isempty(prm.predictedEvt), % feedback call
        prm.trialClassified = true;
        prm.correctTarget = prm.predictedEvt == prm.lastTrialEvent;
        objID = prm.objID(prm.positionMat(:,prm.trialCount)==prm.predictedEvt);
        prm.mostPred=find(prm.objID==objID);        
        setRESI('setRingColor','idx',objID,'red',0.0,'green',0.8,'blue',0.0);
        setRESI('setRingTransparency','idx',objID,'transparency',0);
        notAttended = setdiff(1:prm.nObjects,prm.predictedEvt);
        for k=notAttended,
            objID = prm.objID(prm.positionMat(:,prm.trialCount)==k);
            setRESI('setRingColor','idx',objID,'red',0.8,'green',0.0,'blue',0.0);
            setRESI('setRingTransparency','idx',objID,'transparency',0);
        end
        prm.hitSuccess = prm.trialClassified && prm.correctTarget;   % success if selected is intended event
    elseif prm.mode==0 && ~prm.hitTarget && (prm.curEvent>0), % train run call
        %         % set ring color according to attended object
        %         objID = bci.paradigm.attendObjectID;
        % set ring color according to attended object randomly (70% prob
        % correct)
        eventPool = [setdiff(prm.objID,bci.paradigm.attendObjectID),...
            repmat(bci.paradigm.attendObjectID,1,7)];
        objID = eventPool(ceil(rand(1)*length(eventPool)));
        prm.mostPred=find(prm.objID==objID);
        prm.trialClassified = true;        
        setRESI('setRingColor','idx',objID,'red',0.0,'green',0.8,'blue',0.0);
        setRESI('setRingTransparency','idx',objID,'transparency',0);
        notAttended = setdiff(prm.objID,objID);
        for k=notAttended,
            setRESI('setRingColor','idx',k,'red',0.8,'green',0.0,'blue',0.0);
            setRESI('setRingTransparency','idx',k,'transparency',0.5);
        end
    else
        prm.mostPred=0;
    end
    prm.hitTarget = prm.trialClassified||prm.mode==0; % hit target is true when trial is over
    if prm.hitTarget,
        %end stimulation trial
    end
elseif strcmp(action,'init'),
    prm.maxVisibleFrames = 3;
    prm.objID=0:3;
    ringThickness = 0.005;

    if strcmp(bci.paradigm.objectAlignment,'+'),
        fixCrossPos=[0,0.0];
        diskSize=0.16;
        prm.objXY=[fixCrossPos(1) -0.33;-0.33,fixCrossPos(2);...
            0.33,fixCrossPos(2); fixCrossPos(1),0.33];
        objScaling=[1,1,1,0.7];
        vp=[0,1.162,0.48,-1,0,0.00,1.3];
        bo = [1,0,0,45;1,0,0,-45;0,0,1,90;-1,0,0,120];
    elseif  strcmp(bci.paradigm.objectAlignment,'x'),
        fixCrossPos=[0,0.05];
        diskSize=0.20;
        prm.objXY=[-0.35 -0.3;-0.33,0.33; 0.35,-0.30; 0.33,0.33];
        objScaling=[1,1,1,0.7];
        vp=[0,1.162,0.48,-1,0,0.00,1.3];
        bo = [1,0,0,45;1,0,0,-45;0,0,1,90;-1,0,0,120];
    elseif  strcmp(bci.paradigm.objectAlignment,'-'), % new virtual robot scenario
        fixCrossPos=[0.28,0.0];
        diskSize=0.18;
        prm.objXY=[0.4 -0.28; 0.55,-0.11; 0.55, 0.11; 0.4,0.28];
        objScaling=[1,1,1,1];
        vp = [0.8,0.0,0.56,0.3,0.3,0.9,1.6666];
        bo = [1,0,0,90;1,0,0,90;1,0,0,-90;1,0,0,90];
    else % object recognition mode
        diskSize=0.18;
        vp = [0.8,0.0,0.56,0.3,0.3,0.9,1.6666];
    end
    % initialize control parameters
    prm.trialCount = 0;
    prm.stimCount = 0;
    prm.nObjects = length(prm.objID);
    prm.tobjIdx = 1:prm.nObjects;
    prm.nTrials = prm.nObjects*bci.numTrials;
    flashSeqTempl = bciGenerateFlashSequence(bci.paradigm.nTargetFlashes,prm.nObjects);
    prm.nFlashes = size(flashSeqTempl,1);
    prm.nFlashEvents = size(flashSeqTempl,2);
    firstFlashOnset = 0.5;
    prm.flashDelay = (bci.movDur-firstFlashOnset)./prm.nFlashes;
    prm.flashOnsetTimes = linspace(firstFlashOnset,prm.flashDelay*(prm.nFlashes-1),prm.nFlashes);
    prm.lastFlashSequence = zeros(1,prm.nFlashEvents);
    % flashSequenceMat defines which object flashes at which time
    prm.flashSequenceMat = zeros(prm.nFlashes,prm.nFlashEvents,prm.nTrials);
    % positionMat defines which Object appears at which position
    prm.positionMat = zeros(prm.nObjects,prm.nTrials);

    % set ViewPoint
    setRESI('setViewpoint','x',vp(1),'y',vp(2),'z',vp(3),'rotX',vp(4),...
        'rotY',vp(5),'rotZ',vp(6),'angle',vp(7));

    for k=1:prm.nObjects,
        % object scaling
        setRESI('setBodyScale','idx',prm.objID(k),'scale',objScaling(k));
        % object orientation
        setRESI('setBodyOrientation','idx',prm.objID(k),'x',bo(k,1),'y',bo(k,2),'z',bo(k,3),'angle',bo(k,4));
        % Object positions
        setRESI('setBodyPosition','idx',prm.objID(k),...
            'x',prm.objXY(k,1),'y',prm.objXY(k,2));
        setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',eps,...
            'maxVisibleFrames',prm.maxVisibleFrames);
        %decouple each object
        setRESI('coupleObjectFrequency','idx',prm.objID(k),'otheridx',-1);
        % disk and ring color
        setRESI('setDiskColor','idx',prm.objID(k),'red',1,'green',1,'blue',1);
        setRESI('setRingColor','idx',prm.objID(k),'red',0.2,'green',0.2,'blue',0.2);
        setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
    end
    setRESI('setBodyPosition','idx',4,'x',fixCrossPos(1),'y',fixCrossPos(2)); % fixation cross

    %disk scaling
    setRESI('setDiskDiameter','diameter',diskSize);
    setRESI('setRingThickness','thickness',ringThickness);

    % couple the calibrator at the attended object
    if isfield(bci.paradigm,'attendObjectID'),
        setRESI('coupleCalibratorFrequency','idx',bci.paradigm.attendObjectID);
    end
    prm.trialClassified = false;
    prm.hitTarget = prm.mode==0;
    prm.hitSuccess=false;
elseif strcmp(action,'newevent')
    if ~any(prm.curEvent==bci.eventsToClassify),
        if prm.curEvent==8, % start flash sequence event
            %             prm.nextFlashTime = 0;
            prm.flashCount = 1;
            prm.stimCount = prm.stimCount + 1;
        else
            for k=1:prm.nObjects,
                setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
            end
        end
    else % set position
        % set trial and flash Counter
        prm.trialCount = prm.trialCount + 1;
        % determine where each object appears
        prm.positionMat(prm.objID==bci.paradigm.attendObjectID,prm.trialCount)=prm.curEvent;
        remainingPos = setdiff(1:prm.nObjects,prm.curEvent);
        prm.positionMat(prm.objID~=bci.paradigm.attendObjectID,prm.trialCount)=...
            remainingPos(randperm(prm.nObjects-1));
        % determine a random flash sequence
        prm.flashSequenceMat(:,:,prm.trialCount) = bciGenerateFlashSequence(bci.paradigm.nTargetFlashes,prm.nObjects);
        for k=1:prm.nObjects,
            setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
        end
        % realign the objects
        for objCount=1:prm.nObjects,
            setRESI('setBodyPosition','idx',prm.objID(objCount),...
                'x',prm.objXY(prm.positionMat(objCount,prm.trialCount),1),...
                'y',prm.objXY(prm.positionMat(objCount,prm.trialCount),2));
        end
        prm.lastTrialEvent = prm.curEvent;
    end
    prm.trialClassified = false;
    prm.hitTarget = false;
    prm.hitSuccess = false;
end

% ----------------------------------------
% P300 selection task with gripper control
% ----------------------------------------
function prm=bciParadigmID14(bci,action,prm)
if strcmp(action,'flash'),
    % determine reference object to ensure synchrony of flashes
    refObj = find(prm.flashSequenceMat(prm.flashCount,:,prm.trialCount),1);
    for objCount=find(prm.lastCoupling),
        % decouple previous couplings
        setRESI('coupleObjectFrequency','idx',prm.objID(objCount),'otheridx',-1);
    end    
    if ~isempty(refObj),        
        prm.lastCoupling = prm.flashSequenceMat(prm.flashCount,prm.tobjIdx,prm.trialCount)&(prm.tobjIdx)~=refObj;
        for objCount=find(prm.lastCoupling),
            % couple commonly flashing objects
            setRESI('coupleObjectFrequency','idx',prm.objID(objCount),'otheridx',prm.objID(refObj));
        end
    else
        prm.lastCoupling = false(1,prm.nFlashEvents);
    end
    prm.flashCount = prm.flashCount+1;
    if ~isempty(refObj),
        setRESI('flashObject','idx',prm.objID(refObj),'minVisibleFrames',prm.maxVisibleFrames);
    end
    prm.objectFlashed =~isempty(refObj);
    %     prm.nextFlashTime = prm.nextFlashTime+prm.flashDelay;
elseif strcmp(action,'select'),
    % stop flash sequence
    if prm.curEvent ~= 8,
        prm.hitTarget=false;
        prm.mostPred = 0;
        if prm.gripperState==4,
            prm=bciExecuteGraspFeedback(prm);
        end
        return
    end
    if prm.mode>0 && ~prm.trialClassified && ~isempty(prm.predictedEvt), % feedback call
        % set ring color according to prediction probability
        prm.trialClassified = true;
        prm.correctTarget = prm.predictedEvt == prm.lastTrialEvent;
        objID = prm.objID(prm.predictedEvt);
        prm.mostPred=find(prm.objID==objID);        
        if bci.paradigm.feedbackType > 1,
            while ~prm.readyForNextPrediction,
                prm = bciExecuteGraspFeedback(prm);
            end
            prm.readyForNextPrediction = false;
            prm.currentObject = objID;
            prm = bciExecuteGraspFeedback(prm);
            prm.trialComplete = ~true; % need this variable???

        else
            setRESI('setRingColor','idx',objID,'red',0.0,'green',0.8,'blue',0.0);
            setRESI('setRingTransparency','idx',objID,'transparency',0);
            notAttended = setdiff(1:prm.nObjects,prm.predictedEvt);
            for k=notAttended,
                objID = prm.objID(k);
                setRESI('setRingColor','idx',objID,'red',0.8,'green',0.0,'blue',0.0);
                setRESI('setRingTransparency','idx',objID,'transparency',0);
            end
            prm.trialComplete = true;
        end
        prm.hitSuccess = prm.trialClassified && prm.correctTarget;   % success if selected is intended event
    elseif prm.mode==0 && ~prm.hitTarget && (prm.curEvent>0), % train run call
        %         % set ring color according to attended object
        %         objID = bci.paradigm.attendObjectID;
        % set ring color according to attended object randomly (70% prob
        % correct)
        eventPool = [setdiff(prm.objID,prm.attendObject),...
            repmat(prm.attendObject,1,7)];
        objID = eventPool(ceil(rand(1)*length(eventPool)));
        prm.mostPred=find(prm.objID==objID);
        prm.trialClassified = true;
        setRESI('setRingColor','idx',objID,'red',0.0,'green',0.8,'blue',0.0);
        setRESI('setRingTransparency','idx',objID,'transparency',0);
        notAttended = setdiff(prm.objID,objID);
        for k=notAttended,
            setRESI('setRingColor','idx',k,'red',0.8,'green',0.0,'blue',0.0);
            setRESI('setRingTransparency','idx',k,'transparency',0.5);
        end
    else
        prm.mostPred=0;
    end
    % control gripper
    if prm.gripperState>0,
        prm=bciExecuteGraspFeedback(prm);
    end
    if prm.gripperState==4,
        prm.trialComplete = prm.trialClassified;
    end
    prm.hitTarget =  prm.trialComplete || prm.mode==0;
    %     if prm.isFlickering &&( prm.trialClassified||prm.mode==0),
    % stop stimulation
    %     end
elseif strcmp(action,'init'),
    prm.maxVisibleFrames = 4;
    prm.objID=0:length(bci.eventsToClassify)-1;
    fixCrossID = max(prm.objID) + 1;
    diskSize=0.15; % disk size
    ringThickness = 0.003;
%     vp = [1.17,0.0,0.85,0.285,0.27,0.92,1.67]; % view point
    vp=[0.664, 0.753, 0.926,0.067, 0.408,0.911, 2.766];

    isLowero=double(strcmp(bci.paradigm.objectAlignment,'o'));
    isUpperO=double(strcmp(bci.paradigm.objectAlignment,'O'));
    prm.objectRecognition = isLowero*double(prm.mode~=3)+isUpperO*2+isLowero*double(prm.mode==3)*3; % object recognition mode 
        % 0=no recognition, 1=recognition+physical grasping , 2=offline
        % mode, 3=initialization mode (no physical grasping)
    fixCrossPos=[0.55,0.0]; % fixation cross
    
    if prm.objectRecognition==0, % new virtual robot scenario        
        prm.objXY=[ 0.54 -0.255;0.65,-0.1;0.45 -0.1; 0.65, 0.1;0.45,0.1; 0.54 0.255]; % object position
        prm.objXY=prm.objXY([2 3 4 5 1 6],:);%pos of Noris,apple,schnabel,birne,igs,iff
        objScaling=[1,1,1,1,1,1];   % object size        
        bo = [0,0,0,0;0,0,0,0;0,0,0,0;0,0,0,0;0,0,0,0;0,0,0,0]; % object orientation
    end
    % initialize control parameters
    prm.gripperState = -1; %-1=start 0=default position, 1=gripping,2=pick object 3=replace object, 4=reset gripper
    prm.trialComplete = false;
    prm.stimCount = 0;
    prm.trialCount = 0;
    prm.nObjects = length(prm.objID);
    prm.tobjIdx = 1:prm.nObjects;
    prm.nTrials = prm.nObjects*bci.numTrials;
    flashSeqTempl = bciGenerateFlashSequence(bci.paradigm.nTargetFlashes,prm.nObjects);
    prm.nFlashes = size(flashSeqTempl,1);
    prm.nFlashEvents = size(flashSeqTempl,2);
    firstFlashOnset = 0.5;
    prm.flashDelay = (bci.movDur-firstFlashOnset)./prm.nFlashes;
    prm.flashOnsetTimes = linspace(firstFlashOnset,prm.flashDelay*(prm.nFlashes-1),prm.nFlashes);
    prm.lastCoupling = false(1,prm.nFlashEvents);
    % flashSequenceMat defines which object flashes at which time
    prm.flashSequenceMat = zeros(prm.nFlashes,prm.nFlashEvents,prm.nTrials);
    prm.targetObjID = zeros(1,prm.nTrials);

    % set ViewPoint
    setRESI('setViewpoint','x',vp(1),'y',vp(2),'z',vp(3),'rotX',vp(4),...
        'rotY',vp(5),'rotZ',vp(6),'angle',vp(7));

    for k=1:prm.nObjects,
        if prm.objectRecognition==0,
             % object scaling
            setRESI('setBodyScale','idx',prm.objID(k),'scale',objScaling(k));
            % object orientation
            setRESI('setBodyOrientation','idx',prm.objID(k),'x',bo(k,1),'y',bo(k,2),'z',bo(k,3),'angle',bo(k,4));
            % Object positions
            setRESI('setBodyPosition','idx',prm.objID(k),...
                'x',prm.objXY(k,1),'y',prm.objXY(k,2));
        end
        setRESI('setObjectFrequency','idx',prm.objID(k),'frequency',eps,...
            'maxVisibleFrames',prm.maxVisibleFrames);
        %decouple each object
        setRESI('coupleObjectFrequency','idx',prm.objID(k),'otheridx',-1);
        % disk and ring color
        setRESI('setDiskColor','idx',prm.objID(k),'red',1,'green',1,'blue',1);
        setRESI('setRingColor','idx',prm.objID(k),'red',0.2,'green',0.2,'blue',0.2);
        setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
    end
    if prm.objectRecognition==0,
        setRESI('setBodyPosition','idx',fixCrossID,'x',fixCrossPos(1),'y',fixCrossPos(2)); % fixation cross
    end

    %disk scaling
    setRESI('setDiskDiameter','diameter',diskSize);
    setRESI('setRingThickness','thickness',ringThickness);

    % calibrator flickering off
    setRESI('setCalibratorFrequency','frequency',0,'maxVisibleFrames',0);

    prm.trialClassified = false;
    prm.hitTarget = prm.mode==0;
    prm.hitSuccess=false;
    prm.readyForNextPrediction = true;        
    if prm.objectRecognition==3,
         bciRecognizeObjects();           
    end
    if ~isfield(bci.paradigm,'graspingPrepared'),
        setRESI('prepareGrasping');
        bci.paradigm.graspingPrepared = true;
        fprintf('Grasping prepared.\n');
    end
    if prm.mode==3, % initialize grasp calculation
        bciExecuteGraspCalculation(prm.objID);
    end
elseif strcmp(action,'newevent')
    if ~any(prm.curEvent==bci.eventsToClassify),
        if prm.curEvent==8, % start flash sequence event
            % couple calibrator
            setRESI('coupleCalibratorFrequency','idx',prm.attendObject);            
            % turn cue off
            setRESI('setRingTransparency','idx',prm.attendObject,'transparency',1);
            %             prm.nextFlashTime = 0;
            prm.flashCount = 1;
            prm.stimCount = prm.stimCount + 1;
        else
            % executed at start and end event
            for k=1:prm.nObjects,
                setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
            end
        end
    else % set position
        % set trial and flash Counter
        prm.trialCount = prm.trialCount + 1;
        % determine a random flash sequence
        prm.flashSequenceMat(:,:,prm.trialCount) = bciGenerateFlashSequence(bci.paradigm.nTargetFlashes,prm.nObjects);
        % determine attended Object
        prm.attendObject = prm.objID(prm.curEvent);
        prm.targetObjID(prm.trialCount) = prm.attendObject;
        for k=1:prm.nObjects,
            setRESI('setRingTransparency','idx',prm.objID(k),'transparency',1);
        end
        % show cue for attended object
        if prm.mode==0 || bci.paradigm.feedbackType==0 || bci.paradigm.feedbackType==3,
            % debug: subjects reported missing cues
            fprintf('Cue on obj %i\n',prm.attendObject);
            setRESI('coupleCalibratorFrequency','idx',-1);
            setRESI('flashCalibrator','minVisibleFrames',30);
            % turn cue on
            setRESI('setRingColor','idx',prm.attendObject,'red',0.9,'green',0.9,'blue',0.9);
            setRESI('setRingTransparency','idx',prm.attendObject,'transparency',0);
        end
%         % couple calibrator
%         setRESI('coupleCalibratorFrequency','idx',prm.attendObject);
        prm.lastTrialEvent = prm.curEvent;
    end
    prm.trialClassified = false;
    prm.trialComplete = false;
    prm.hitTarget = false;
    prm.hitSuccess = false;
end
